Imports Aricie.DNN.Caching
Imports Aricie.ComponentModel
Imports System.Threading
Imports Aricie.Collections

Namespace Aricie.DNN.Caching.Client
    Public MustInherit Class CachingClientProvider(Of T As {CachingClientHostInfo, New}, Y As {CachingClusterSettings, New})
        Inherits CachingClientProvider



        Public Overrides Function GetNewCluster() As CachingClusterSettings
            Return New Y
        End Function

        Public Overrides Function GetNewHostItem() As CachingClientHostInfo
            Return New T
        End Function

        Public ReadOnly Property SpecificClusterSettings() As Y
            Get
                Return DirectCast(Me.ClusterSettings, Y)
            End Get
        End Property

       


    End Class


    Public MustInherit Class CachingClientProvider
        Implements IDisposable


        'Public Event Errored As EventHandler(Of Aricie.ComponentModel.GenericEventArgs(Of KeyValuePair(Of String, String)))


        Private _LastErrors As New List(Of KeyValuePair(Of String, String))
        Private _CurrentStatus As DistributedCachingStatus = DistributedCachingStatus.Stopped



        Private _Disabled As Boolean
        Private _EnableTimer As System.Threading.Timer
        Private _ProviderSettings As CachingClientProviderInfo
        Private _ClusterSettings As CachingClusterSettings
        Private _ItemVersions As New Dictionary(Of String, Object)
        Private _TagsCatalog As SerializableDictionary(Of String, List(Of String))
        Friend UpdateTagsCatalog As Boolean
        Private _RetrievingTagsCatalog As Boolean
        Private Shared _EmptyTagsCatalog As New SerializableDictionary(Of String, List(Of String))

        Private Sub CatalogRemovedHandler(ByVal key As String, ByVal value As Object, ByVal reason As CacheItemRemovedReason)
            _TagsCatalog = Nothing
        End Sub

        Friend ReadOnly Property TagsCatalog() As SerializableDictionary(Of String, List(Of String))
            Get
                If _TagsCatalog Is Nothing Then
                    If _RetrievingTagsCatalog Then
                        Return _EmptyTagsCatalog
                    Else
                        _RetrievingTagsCatalog = True
                        _TagsCatalog = DirectCast(DistributedCachingProviderBase.Instance.GetItem(DistributedCachingProviderBase.Settings.CloudSettings.TagsCatalogKey), SerializableDictionary(Of String, List(Of String)))
                        If _TagsCatalog Is Nothing Then
                            _TagsCatalog = New SerializableDictionary(Of String, List(Of String))
                            DistributedCachingProviderBase.Instance.Insert(DistributedCachingProviderBase.Settings.CloudSettings.TagsCatalogKey, _TagsCatalog, _
                                    Nothing, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, New CacheItemRemovedCallback(AddressOf CatalogRemovedHandler), False)
                        End If
                        _RetrievingTagsCatalog = False
                    End If
                End If
                Return _TagsCatalog
            End Get
        End Property

        Friend Sub AddTag(ByVal key As String, ByVal tag As String)
            Dim keys As List(Of String) = Nothing
            If Not Me.TagsCatalog.TryGetValue(tag, keys) Then
                keys = New List(Of String)
            End If
            If Not keys.Contains(key) Then
                keys.Add(key)
                Me.TagsCatalog(tag) = keys
                UpdateTagsCatalog = True
            End If
        End Sub


        Protected Sub SetItemVersion(ByVal key As String, ByVal version As Object)
            SyncLock Me._ItemVersions
                Me._ItemVersions(key) = version
            End SyncLock
        End Sub

        Protected Function GetItemVersion(ByVal key As String) As Object
            Dim toReturn As Object = Nothing
            Me._ItemVersions.TryGetValue(key, toReturn)
            Return toReturn
        End Function

        Friend Sub RemoveItemVersion(ByVal key As String)

            If _ItemVersions.ContainsKey(key) Then
                SyncLock _ItemVersions
                    Me._ItemVersions.Remove(key)
                End SyncLock
            End If

        End Sub

        'Protected Friend Function SplitTags(ByVal tags As String) As String()
        '    Return Common.ParseStringList(tags.ToLower).ToArray
        'End Function


        Public ReadOnly Property CurrentStatus() As DistributedCachingStatus
            Get
                Return _CurrentStatus
            End Get
        End Property


        Public ReadOnly Property LastErrors() As List(Of KeyValuePair(Of String, String))
            Get
                Dim toReturn As New List(Of KeyValuePair(Of String, String))(_LastErrors)
                _LastErrors.Clear()
                Return toReturn
            End Get
        End Property




        Public Property ProviderSettings() As CachingClientProviderInfo
            Get
                Return _ProviderSettings
            End Get
            Set(ByVal value As CachingClientProviderInfo)
                _ProviderSettings = value
            End Set
        End Property

        Public Property ClusterSettings() As CachingClusterSettings
            Get
                Return _ClusterSettings
            End Get
            Set(ByVal value As CachingClusterSettings)
                _ClusterSettings = value
            End Set
        End Property

        Public ReadOnly Property Disabled() As Boolean
            Get
                Return _Disabled
            End Get
        End Property


      

      

        Public Sub AddError(ByVal statusAndMessage As KeyValuePair(Of String, String))
            Me._LastErrors.Add(statusAndMessage)
        End Sub

        Public MustOverride Function GetNewCluster() As CachingClusterSettings

        Public MustOverride Function GetNewHostItem() As CachingClientHostInfo


        Public MustOverride Function [Get](ByVal objCacheJob As CacheJob) As WrappedCacheObject


        Public MustOverride Function Put(ByVal objCacheJob As CachePutJob) As Boolean

        Public MustOverride Sub Remove(ByVal objJob As CacheJob)

        ''' <summary>
        ''' Get objets by tags
        ''' </summary>
        ''' <param name="objCacheJob"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public MustOverride Function MultipleGet(ByVal objCacheJob As CacheJob) As IEnumerable(Of KeyValuePair(Of String, WrappedCacheObject))

        Public MustOverride Function AddCallBack(ByVal objCacheJob As CacheJob, ByVal itemRemovedCallback As CacheItemRemovedCallback) As Boolean

        Public MustOverride Sub ResetTimeOut(ByVal objCacheJob As CachePutJob)

        Public Overridable Sub [Stop]()

            _Disabled = True
            If Me._EnableTimer IsNot Nothing Then
                'AndAlso Interlocked.CompareExchange(Me._EnableTimer, Nothing, Me._EnableTimer).Equals(Me._EnableTimer) Then
                Me._EnableTimer.Dispose()
            End If
            Aricie.Services.CacheHelper.ClearCache()
        End Sub


        Public MustOverride Sub CreateRegion(ByVal objCacheJob As CacheJob)

        Public MustOverride Sub ClearRegion(ByVal objCacheJob As CacheJob)

        Public MustOverride Sub DeleteRegion(ByVal objCacheJob As CacheJob)


        Friend Sub SetCurrentStatus(ByVal status As DistributedCachingStatus)
            _CurrentStatus = status
        End Sub

        Public Sub DisableTemporary()
            If Not _Disabled Then

                Me.Stop()

                SetCurrentStatus(DistributedCachingStatus.InternalError)
                _EnableTimer = New Timer(New TimerCallback(AddressOf TryReEnableCallBack), Nothing, Me.ClusterSettings.RecoverDuration.Value, TimeSpan.FromMilliseconds(-1))
            End If
        End Sub

        Public Sub ProcessException(ByVal ex As Exception, status As String, disable As Boolean)
            If disable Then
                Me.DisableTemporary()
            End If
            Me.AddError(New KeyValuePair(Of String, String)(status, ex.ToString()))
            If DistributedCachingProviderBase.Settings.LoggingSettings.EnableDebugLogging Then
                Aricie.DNN.Diagnostics.AsyncLogger.Instance.AddException(ex)
            End If
        End Sub

        Private _InTimerCallback As Integer

        Private Sub TryReEnableCallBack(ByVal state As Object)
            If Interlocked.Exchange(Me._InTimerCallback, 1) <> 0 Then
                Exit Sub
            End If
            Try
                If Disabled Then
                    _Disabled = False

                    If Me._EnableTimer IsNot Nothing Then
                        Me._EnableTimer.Dispose()
                    End If

                    Me.SetCurrentStatus(DistributedCachingStatus.Stopped)
                End If
            Finally
                Interlocked.Exchange(_InTimerCallback, 0)
            End Try

        End Sub

        Protected Function ConvertObjectToWrappedPair(ByVal source As KeyValuePair(Of String, Object)) As KeyValuePair(Of String, WrappedCacheObject)
            Return New KeyValuePair(Of String, WrappedCacheObject)(source.Key, DirectCast(source.Value, WrappedCacheObject))
        End Function

        Protected Function GetTagKeys(ByVal objCacheJob As CacheJob) As IEnumerable(Of String)
            Dim toReturn As New Dictionary(Of String, Boolean)
            Dim tempKeys As List(Of String) = Nothing
            For Each tag As String In objCacheJob.Strategy.GetTagsList
                If Me.TagsCatalog.TryGetValue(tag, tempKeys) Then
                    For Each key As String In tempKeys
                        toReturn(key) = True
                    Next
                End If
            Next
            Return toReturn.Keys
        End Function

        Private disposedValue As Boolean = False        ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    Me.Stop()
                End If

            End If
            Me.disposedValue = True
        End Sub

#Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

    End Class
End Namespace